iT邦幫忙

2025 iThome 鐵人賽

DAY 3
0
DevOps

30 天帶你實戰 LLMOps:從 RAG 到觀測與部署系列 第 3

Day03 - 環境準備:Docker + Conda

  • 分享至 

  • xImage
  •  

🔹 前言

昨天(Day 2)我們扮演了 PM,把專案的需求、目標和架構拆解清楚。
今天開始,就要進入實作階段了,專案程式碼已放在 GitHub,之後如果有 Demo 也都會放在上面,有興趣的讀者可以自行 fork 來改寫、實驗。

在正式進入 RAG Pipeline 之前,我們需要一個 可重現、乾淨的開發環境
這裡我們提供兩條路徑,讀者可以依照自己的需求選擇:

  • 路徑 A:本地 Python + Conda → 最輕量,直接啟動 Conda 環境跑程式。
  • 路徑 B:Docker + Conda → 把環境封裝在容器裡,方便多人合作與跨機器一致性。

接下來的文章裡,大多數 Demo 都能直接在 路徑 A 下完成;
如果你需要更穩定的團隊環境或部署到伺服器,再切換到 路徑 B 即可。

https://ithelp.ithome.com.tw/upload/images/20250917/20120069EERPf4jfKN.png

  • Docker:確保所有人跑出的環境一致,不怕「在我電腦能跑」的問題。
  • Conda:方便管理 Python 套件版本(比 pip 更適合需要科學計算、GPU 支援的場景)。
  • 兩者結合 → 用 Docker 提供最外層容器,用 Conda 管理容器內的 Python 環境。

🔹 專案目錄結構

day03_environments_prepare/
├── docker/
│   └── Dockerfile
├── env/
│   └── environment.yml
├── src/
│   └── app.py
└── README.md
  • docker/Dockerfile:定義環境。
  • env/environment.yml:Conda 環境需求。
  • src/app.py:主程式。
  • README.md:專案說明。

🔹Conda 環境設定

🛠 Conda 安裝與設定(macOS)

這個專案需要一個乾淨的 Python 環境,我們推薦使用 Miniforge(由社群維護的 Conda 發行版,比 Anaconda 輕量,而且預設支援 conda-forge channel)。因為筆者自己的電腦是 Macbook Air,這邊僅列出筆者自己的安裝步驟:

  1. 使用 Homebrew 安裝:
brew install --cask miniforge
  1. 設定 Shell:
echo 'export PATH="$HOME/miniforge3/bin:$PATH"' >> ~/.zshrc
source ~/.zshrc
  1. 檢查是否安裝成功:
conda --version
  1. 寫好要安裝的套件們,並且放在 env/environment.yml
name: day03_environments
channels:
  - defaults
  - conda-forge
dependencies:
  - python=3.10
  - pip
  - pip:
      - fastapi
      - uvicorn
      - langchain
      - openai
      - weaviate-client
      - jupyter

  1. 建立專案環境:
conda env create -f env/environment.yml 
conda activate day03_environments

💡 如果已經有其他 Conda 版本(例如 Anaconda 或 Miniconda),也能沿用,只要確保能成功建立 day03_environments 環境即可。

💡 為什麼不用 venv,而是選 Conda?

Python 其實內建了 venv,也能做環境隔離,那為什麼我們要用 Conda?

venv 的特色

  • 輕量、內建,不需要額外安裝
  • 功能主要是「隔離 Python 套件」,避免全域 pip 汙染
  • 適合單純 Web 專案(Flask、Django、FastAPI API)

Conda 的優勢

  • Python 版本管理:輕鬆切換 3.7、3.9、3.11
  • 支援非 Python 套件:像是 R、C/C++、CUDA、MKL
  • 安裝更穩定:AI/ML 常見套件(faiss、pymilvus)有預編譯 binary
  • 可攜性好environment.yml 能完整描述環境,比 requirements.txt 更穩定

對照表

功能 venv/virtualenv conda
Python 版本管理 ❌ 系統決定 conda create -n py39 python=3.9
安裝 C/C++/GPU 套件 ❌ 需手動編譯 ✅ 預先提供 binary,安裝穩定
跨語言支援 ❌ 只管 Python ✅ 支援 R, Julia, C, CUDA 等
環境描述檔 requirements.txt environment.yml(更完整)
輕量程度 ✅ 超輕量 ❌ 稍微重一些,但更完整

後面每一天的文章,我會建議讀者把 conda 環境獨立建置和啟動。


🔹Dockerfile 範例

(這邊依照自己習慣的方式開發即可,也可以先不用容器化)

建立 docker/Dockerfile

FROM continuumio/miniconda3:23.5.2-0

WORKDIR /app

# 複製環境需求
COPY env/environment.yml .

# 建立 conda 環境
RUN conda env create -f environment.yml

# 預設啟用 conda 環境
SHELL ["conda", "run", "-n", "day03_environments", "/bin/bash", "-c"]

# 複製專案程式碼
COPY src/ ./src 

# 預設啟動 FastAPI app
CMD ["conda", "run", "-n", "day03_environments", "uvicorn", "src.app:app", "--host", "0.0.0.0", "--port", "8000"]

建立環境所需套件 env/environment.yml

name: day03_environments
channels:
  - defaults
  - conda-forge
dependencies:
  - python=3.10
  - pip
  - pip:
      - fastapi
      - uvicorn
      - langchain
      - weaviate-client
      - jupyter
      - openai
      - python-dotenv

新增一個 src/app.py

from fastapi import FastAPI

app = FastAPI()

@app.get("/")
def read_root():
    return {"Hello": "LLMOps!"}

建置與執行 (用 podman 也可):

docker build -t day03_environments -f docker/Dockerfile .
docker run -it --rm -p 8000:8000 day03_environments

如果想要先build 再掛載 app.py 進去,方便隨時修改的話,可以用 `docker run 的方式:

docker build --build-arg MODE=dev -t day03_environments -f docker/Dockerfile .

docker run -it --rm -p 8000:8000 \
  -v "$(pwd)/src:/app/src" \
  day03_environments

Hot-reload 版的 Dockerfile :

FROM continuumio/miniconda3:23.5.2-0

# ── 基本設定 ────────────────────────────────────────────────
WORKDIR /app
ENV PYTHONUNBUFFERED=1 \
PYTHONDONTWRITEBYTECODE=1 \
PYTHONPATH=/app


# 可用 MODE=dev 啟用 --reload;預設 prod
ARG MODE=prod
ENV MODE=${MODE}

# ── 安裝專案環境 ────────────────────────────────────────────

COPY env/environment.yml .
RUN conda env create -f environment.yml

# 之後的 RUN/CMD/ENTRYPOINT 都在指定環境內執行
SHELL ["conda", "run", "-n", "day03_environments", "/bin/bash", "-c"]

# ── 複製程式碼(開發時可用 volume 掛載覆蓋) ───────────────
COPY src/ ./src

# ── 對外連接埠 ─────────────────────────────────────────────
EXPOSE 8000

# ── 依據 MODE 切換啟動參數 ─────────────────────────────────

# prod: 不加 --reload;dev: 加 --reload
CMD if [ "$MODE" = "dev" ]; then \
		uvicorn --app-dir /app src.app:app --host 0.0.0.0 --port 8000 --reload; \
	else \
		uvicorn --app-dir /app src.app:app --host 0.0.0.0 --port 8000; \
	fi

測試環境是否正常:

啟動後訪問 `http://localhost:8000/` → 看到 `{"Hello": "LLMOps!"}` 表示環境成功。

🔹 開始使用 OpenAI 的 API 吧!

接下來的測試,會需要使用 OpenAI API,需要先到 OpenAI API Keys 頁面 建立一組 Key。
建立後,把它存到 .env 檔裡:

export OPENAI_API_KEY=sk-xxxxxxx

在程式裡透過套件呼叫 OpenAI API:

from openai import OpenAI
import os
from dotenv import load_dotenv

load_dotenv()
client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))

⚠️ 計費提醒

  • 現在 (2025) 幾乎沒有免費額度,必須 綁定信用卡 才能使用 API
  • OpenAI 採用 Pay-as-you-go(隨用隨付) 模式:依照實際 Token 使用量計費,每個模型的單價不同(例如 gpt-4o vs gpt-4o-mini)。

💡 如何避免超出預算?

  • 設定硬性上限 (Usage Limits)
    • Billing → Usage limits 頁面,可以設定「Monthly budget(每月上限)」與「Hard limit(硬性上限)」。
    • 一旦達到 Hard limit,API 就會自動停用,不會繼續扣款。
  • 監控用量 (Usage Dashboard)
    • Usage 頁面 可以看到每天的 Token 與金額分佈。
    • 如果要更自動化,可以用 API 查詢用量,或在應用程式裡自己記錄 Token 消耗。
  • 程式內限制
    • 控制單次請求的 max_tokens,例如 Demo 設定在 300 tokens 以內。
    • 避免不必要的長 Prompt,或先用小模型處理,再丟進大模型。

⚠️ 小提醒
本系列中的程式碼與 Demo,已經盡量使用簡化、節省費用的設定(例如限制 max_tokens、選用較便宜的模型),讓大家能安心跟著練習。
不過,如果讀者在實作時輸入大量資料,或改用高單價模型,可能會導致 API 成本比預期高。
這部分請讀者自行調整的使用情境,作者無法保證費用一定維持在同一水平。
建議大家在開始測試前,先到 Usage Limits 設定好每月預算上限,這樣就能更放心地探索,不怕花費失控。 🌱

💡 我想用其他供應商的 LLM 可以嗎?

本系列的測試大部分會示範 OpenAI API。讀者如果想嘗試 其他 LLM 供應商(例如 Anthropic Claude、Google Gemini、Mistral、AWS Bedrock、Azure OpenAI Service 等),也完全可行。本系列的程式碼示範只是「範例骨架」,讀者可以替換成自己偏好的模型與 API 🙂

⚠️ 只需要注意:

  1. API 介面是否相容

    • 只要供應商有提供 REST API / SDK,就能套用到相同的程式流程。
    • 常見參數(例如 modelmax_tokenstemperature)在不同平台名稱可能略有差異。
  2. 計費方式

    • 雖然 LLM 供應商大多有提供「Pay-as-you-go」的付費模式,但不同供應商的 Token 單價、計費單位、免費額度不一樣,要特別留意。
  3. 部署位置

    • 有些供應商支援 雲端 API,有些(例如 Hugging Face Inference 或本地模型)可以跑在 自建環境
    • 如果在意 資料隱私,可以選擇本地化模型或私有部署方案。

🔹 Demo 1:Embedding Test

我們先來寫個測試程式看看能不能產生 Embedding。OpenAI 的 Embedding API 丟進去一串文字,他會回傳一串浮點數,代表某種抽象的語意特徵,語意相近的文字,回傳的浮點數也會相近。詳細的資訊會在第五天做介紹,這邊我們先試著呼叫 API 就好。

# 用 conda 創建一個新的專案環境
❯ conda env create -f ./env/environment.yml

# 啟動專案環境
❯ conda activate day03_environments

# 把 OpenAI API key 寫到環境變數裡
#(僅本次 session 有效,若要長期使用可寫進 `.env` 或 shell 啟動檔)
export OPENAI_API_KEY=sk-xxxxxxx

# 呼叫測試程式碼
❯ python test_embedding.py
Cosine similarity: 0.2016753894756205

test_scripts/test_embedding.py

from openai import OpenAI
from dotenv import load_dotenv
import numpy as np
import os

  

load_dotenv()
client = OpenAI()

# 讀取 .env 檔案裡面的環境變數
api_key = os.getenv("OPENAI_API_KEY")
if not api_key:
	raise ValueError("沒有找到 OPENAI_API_KEY,請檢察環境變數!")

# 比較這兩個字串的相近程度
texts = ["鐵人賽好熱血", "我想寫一個 QA Bot"]
embeddings = [client.embeddings.create(model="text-embedding-3-small", input=t).data[0].embedding for t in texts]

similarity = np.dot(embeddings[0], embeddings[1]) / (np.linalg.norm(embeddings[0]) * np.linalg.norm(embeddings[1]))
print("Cosine similarity:", similarity)

執行後應該會輸出一個 0–1 之間的數值,代表兩句話的相似度。


🔹 Demo 2:Mini QA Bot API

最後,我們用 FastAPI 做一個最小的 QA API,部署圖(Deployment Diagram)如下:

https://ithelp.ithome.com.tw/upload/images/20250917/201200694a2rfaNMVp.png

修改 src/app.py ,添加完整的程式碼:

from fastapi import FastAPI
from openai import OpenAI
from dotenv import load_dotenv
import os

# 載入 .env
load_dotenv()

# 讀取金鑰
api_key = os.getenv("OPENAI_API_KEY")
if not api_key:
raise RuntimeError("❌ 找不到 OPENAI_API_KEY,請確認 .env 或環境變數設定")

app = FastAPI()
client = OpenAI(api_key=api_key)

@app.get("/")
def health():
	return {"ok": True}

# 正式情境我們會用 POST & JSON Body 來處理需要編碼的字串
@app.get("/ask")
def ask(q: str):
	resp = client.chat.completions.create(
		model="gpt-4o-mini",
		messages=[{"role": "user", "content": q}]
	)

	return {"question": q, "answer": resp.choices[0].message.content}

docker run 啟動容器:

# 先把 API key 寫進去設定檔案
echo "OPENAI_API_KEY=sk-proj-XXX(你的 OpenAI API key)" >> .env

# 啟動容器
docker run -it --rm -p 8000:8000 \
	 -v "$(pwd)/src:/app/src" \
	 -v "$(pwd)/.env:/app/.env:ro" \
	 day03_environments

或是用 uvicorn 跑起來 :

uvicorn src.app:app --reload --port 8000

測試:

# 繁體中文要先編碼,否則會被判定成非法的 HTTP request
curl -G --data-urlencode "q=什麼是LLMOps?請用繁體中文回答我" http://localhost:8000/ask

結果:

{"question":"什麼是LLMOps?請用繁體中文回答我","answer":"LLMOps(Large Language Model Operations)是一種針對大型語言模型(LLM)進行管理和運營的綜合方法和流程。隨著大型語言模型的發展和應用越來越廣泛,LLMOps的概念也應運而生,旨在提高這些模型的開發、部署和維護效率。\n\nLLMOps包括以下幾個關鍵方面:\n\n1. **模型管理**:針對大型語言模型的版本控制和訓練流程的管理,確保模型的可靠性和可重複性。\n\n2. **數據管理**:合理管理用於訓練和評估模型的數據集,包括數據的清理、標註以及更新。\n\n3. **環境配置**:建立和維護運行大型語言模型所需的計算環境和基礎設施,確保模型能在最佳性能下運行。\n\n4. **監控與評估**:持續監控模型在實際應用中的表現,及時識別和解決問題,以確保模型的穩定性和準確性。\n\n5. **協同合作**:促進跨團隊間的協作,確保開發、運營和產品團隊之間的有效溝通與合作。\n\nLLMOps的目的是提高大型語言模型的可用性和效能,使企業和組織能夠更有效地利用這些強大的工具來解決實際問題。"}

🎉 恭喜!我們已經有了第一個能回答問題的小助手。


🔹 小結

  • 今天我們建了 乾淨環境
  • 明天(Day 4) 我們會開始比較 向量資料庫(Weaviate、Pinecone、FAISS、Milvus),並把它們接進這個環境。

📚 引用來源 / 延伸閱讀


上一篇
Day02 - 系列專案介紹:企業知識庫 QA Chatbot
下一篇
Day04 - 向量資料庫(Vector Database)- 常見選項與實務比較
系列文
30 天帶你實戰 LLMOps:從 RAG 到觀測與部署4
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言